<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>


<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />

<meta name="theme-color" content="#f8f5ec" />
<meta name="msapplication-navbutton-color" content="#f8f5ec">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="#f8f5ec">



  <meta name="description" content="python开发时邮箱验证,短信验证,图片验证码的实现"/>




  <meta name="keywords" content="flask, python, 安全, 八一" />



  <meta name="baidu-site-verification" content="HhUstaSjr0" />



  <meta name="google-site-verification" content="UA-102975942-1" />






  <link rel="alternate" href="/atom.xml" title="八一">




  <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=2.6.0" />



<link rel="canonical" href="https://bay1.top/2017/11/04/几种开发时安全验证的实现/"/>


<link rel="stylesheet" type="text/css" href="/css/style.css?v=2.6.0" />
<link rel="stylesheet" type="text/css" href="/css/prettify.css" media="screen" />
<link rel="stylesheet" type="text/css" href="/css/sons-of-obsidian.css" media="screen" />



  <link rel="stylesheet" type="text/css" href="/lib/fancybox/jquery.fancybox.css" />




  
  <script id="baidu_analytics">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?9a885cc9fb6cd7bcef579deb8efe8a70";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>



  <script id="google_analytics">
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
        })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

        ga('create', 'UA-102975942-1', 'auto');
        ga('send', 'pageview');
  </script>










    <title> python开发时邮箱验证,短信验证,图片验证码的实现 - 八一 </title>
  </head>

  <body><div id="mobile-navbar" class="mobile-navbar">
  <div class="mobile-header-logo">
    <a href="/." class="logo">八一</a>
  </div>
  <div class="mobile-navbar-icon">
    <span></span>
    <span></span>
    <span></span>
  </div>
</div>

<nav id="mobile-menu" class="mobile-menu slideout-menu">
  <ul class="mobile-menu-list">
    
      <a href="/archives">
        <li class="mobile-menu-item">
          
          
            文章
          
        </li>
      </a>
    
      <a href="/tags">
        <li class="mobile-menu-item">
          
          
            标签
          
        </li>
      </a>
    
      <a href="/about">
        <li class="mobile-menu-item">
          
          
            关于/友链
          
        </li>
      </a>
    
      <a href="/search">
        <li class="mobile-menu-item">
          
          
            站内搜索
          
        </li>
      </a>
    
  </ul>
</nav>

    <div class="container" id="mobile-panel">
      <header id="header" class="header"><div class="logo-wrapper">
  <a href="/." class="logo">八一</a>
</div>

<nav class="site-navbar">
  
    <ul id="menu" class="menu">
      
        <li class="menu-item">
          <a class="menu-item-link" href="/archives">
            
            
              文章
            
          </a>
        </li>
      
        <li class="menu-item">
          <a class="menu-item-link" href="/tags">
            
            
              标签
            
          </a>
        </li>
      
        <li class="menu-item">
          <a class="menu-item-link" href="/about">
            
            
              关于/友链
            
          </a>
        </li>
      
        <li class="menu-item">
          <a class="menu-item-link" href="/search">
            
            
              站内搜索
            
          </a>
        </li>
      
    </ul>
  
</nav>

      </header>

      <main id="main" class="main">
        <div class="content-wrapper">
          <div id="content" class="content">
            
  
  <article class="post">
    <header class="post-header">
      <h1 class="post-title">
        
          python开发时邮箱验证,短信验证,图片验证码的实现
        
      </h1>

      <div class="post-meta">
        <span class="post-time">
          2017-11-04
        </span>
        
        
        
      </div>
    </header>

    
    
  <div class="post-toc" id="post-toc">
    <h2 class="post-toc-title">文章目录</h2>
    <div class="post-toc-content">
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#邮箱验证"><span class="toc-text">邮箱验证</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#图片验证码"><span class="toc-text">图片验证码</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#短信验证"><span class="toc-text">短信验证</span></a></li></ol>
    </div>
  </div>


    <div class="post-content">
      
        <p>额,一个突然的交流让我想起来我耽搁许久各种验证的实现迟迟没做过<br>趁着这个机会就搞了一下<br>分为三部分:邮箱验证,短信验证,图片验证码 <a id="more"></a></p>
<h2 id="邮箱验证"><a href="#邮箱验证" class="headerlink" title="邮箱验证"></a>邮箱验证</h2><p>这个部分是主要参考的经典书籍-狗书<br>思路就是根据用户某些信息通过JSON Web签名生成token,然后再发送邮件验证,经典思路<br>生成和验证函数都加载在模型中<br><a href="https://github.com/bayi27/PythonSecurity/tree/master/token" target="_blank" rel="noopener">完整代码</a><br><a href="http://itsdangerous.readthedocs.io/en/latest/" target="_blank" rel="noopener">itsdangerous中文文档</a>这里介绍了几种签名方式</p>
<blockquote>
<p>token生成和验证<br>TimedJSONWebSignatureSerializer,看这个表面词的意思可以看出这里序列化加入了当前时间<br>这也是实现设置过期时间的依据吧<br>查看itsdangerous源码可以看到具体的加密方式</p>
</blockquote>
<figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> itsdangerous <span class="keyword">import</span> (TimedJSONWebSignatureSerializer <span class="keyword">as</span> Serializer, BadSignature, SignatureExpired)</span><br><span class="line">...</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span><span class="params">(db.Model)</span>:</span></span><br><span class="line">    __tablename__ = <span class="string">'user'</span></span><br><span class="line">    id = db.Column(db.Integer,primary_key=<span class="keyword">True</span>)</span><br><span class="line">    name=db.Column(db.String(<span class="number">64</span>),unique=<span class="keyword">True</span>,index=<span class="keyword">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">genter_auth_token</span><span class="params">(self,expiration=<span class="number">300</span>)</span>:</span> <span class="comment">#设置有效期</span></span><br><span class="line">        s=Serializer(current_app.config[<span class="string">'SECRET_KEY'</span>],expires_in=expiration)</span><br><span class="line">        <span class="keyword">return</span> s.dumps(&#123;<span class="string">'code'</span>:self.name&#125;) <span class="comment">#将用户名当作签名对象</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @staticmethod</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">verify_auth_token</span><span class="params">(token)</span>:</span></span><br><span class="line">        s=Serializer(current_app.config[<span class="string">'SECRET_KEY'</span>])</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            data=s.loads(token) <span class="comment">#加载数据</span></span><br><span class="line">        <span class="keyword">except</span> BadSignature:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">None</span></span><br><span class="line">        <span class="keyword">except</span> SignatureExpired:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">None</span></span><br><span class="line">        <span class="keyword">return</span> data</span><br></pre></td></tr></table></figure>
<p><img src="https://s1.ax1x.com/2018/01/01/pSWgPO.md.png" alt="结果"></p>
<p>同时这里itdangerous类的签名方式都可以接收一个salt<br>文档中这样描述了salt的作用:</p>
<blockquote>
<p>itsdangerous中的盐，是为了一个截然不同的目的而产生的。你可以将它视为成命名空间<br>假设你想签名两个链接。你的系统有个激活链接，用来激活一个用户账户，并且你有一个升级链接，可以让一个用户账户升级为付费用户，这两个链接使用email发送。在这两种情况下，如果你签名的都是用户ID，那么该用户可以在激活账户和升级账户时，复用URL的可变部分。现在你可以在你签名的地方加上更多信息（如升级或激活的意图），但是你也可以用不同的盐</p>
</blockquote>
<p>即只有使用相同盐的序列化器才能成功把值加载出来</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">genter_auth_token</span><span class="params">(self,expiration=<span class="number">300</span>)</span>:</span></span><br><span class="line">	s=Serializer(current_app.config[<span class="string">'SECRET_KEY'</span>],salt=<span class="string">'activate-salt'</span>,expires_in=expiration)</span><br><span class="line">    <span class="keyword">return</span> s.dumps(&#123;<span class="string">'code'</span>:self.name&#125;)</span><br><span class="line"></span><br><span class="line"><span class="meta">@staticmethod</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">verify_auth_token</span><span class="params">(token)</span>:</span></span><br><span class="line">    s=Serializer(current_app.config[<span class="string">'SECRET_KEY'</span>],salt=<span class="string">'activate-salt'</span>)</span><br></pre></td></tr></table></figure>
<h2 id="图片验证码"><a href="#图片验证码" class="headerlink" title="图片验证码"></a>图片验证码</h2><p>这个验证码可以直接调用一些平台的智能验证,也可以用另一种<br>另一个也许是比较传统的思路,就是自己生成的图片水印,保存验证码<br>python和php里都有相应的图片操作方法,这里就写下python的</p>
<blockquote>
<p>流程就是生成任意的数字,保存,添加图片水印</p>
</blockquote>
<p>这里肯定要用的python强大的图片处理库PIL,其中用到了<br>加线条,滤镜等增加干扰<br>下面是完整代码,该做注释的地方我已经加了注释<br>看代码之前,最好先好好看下PIL官方文档,和一些基本概念<br>部分我参考的博文也贴在了文末</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python </span></span><br><span class="line"><span class="comment">#coding=utf-8</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask,send_from_directory</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image,ImageFont,ImageDraw,ImageFilter</span><br><span class="line"></span><br><span class="line">app=Flask(__name__)</span><br><span class="line">app.debug=<span class="keyword">True</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">picture</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line">        self.size = (<span class="number">240</span>,<span class="number">60</span>)</span><br><span class="line">        self.mode=<span class="string">"RGB"</span></span><br><span class="line">        self.color=<span class="string">"white"</span></span><br><span class="line">        self.font = ImageFont.truetype(<span class="string">"C:\Windows\Fonts\Arial.ttf"</span>, <span class="number">36</span>) <span class="comment">#设置字体大小</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">randChar</span><span class="params">(self)</span>:</span></span><br><span class="line">        basic=<span class="string">'23456789abcdefghijklmnpqrstwxyzABCDEFGHIJKLMNPQRSTWXYZ'</span></span><br><span class="line">        <span class="keyword">return</span> basic[random.randint(<span class="number">0</span>,len(basic)<span class="number">-1</span>)] <span class="comment">#随机字符</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">randBdColor</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> (random.randint(<span class="number">64</span>,<span class="number">255</span>),random.randint(<span class="number">64</span>,<span class="number">255</span>),random.randint(<span class="number">64</span>,<span class="number">255</span>)) <span class="comment">#背景</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">randTextColor</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> (random.randint(<span class="number">32</span>, <span class="number">127</span>), random.randint(<span class="number">32</span>, <span class="number">127</span>), random.randint(<span class="number">32</span>, <span class="number">127</span>)) <span class="comment">#随机颜色</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">proPicture</span><span class="params">(self)</span>:</span></span><br><span class="line">        new_image=Image.new(self.mode,self.size,self.color) <span class="comment">#创建新图像有三个默认参数:尺寸,颜色,模式</span></span><br><span class="line">        drawObject=ImageDraw.Draw(new_image) <span class="comment">#创建一个可以对image操作的对象</span></span><br><span class="line">        line_num = random.randint(<span class="number">4</span>,<span class="number">6</span>) <span class="comment"># 干扰线条数</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> range(line_num):</span><br><span class="line">            <span class="comment">#size=(240,60)</span></span><br><span class="line">            begin = (random.randint(<span class="number">0</span>, self.size[<span class="number">0</span>]), random.randint(<span class="number">0</span>, self.size[<span class="number">1</span>]))</span><br><span class="line">            end = (random.randint(<span class="number">0</span>, self.size[<span class="number">0</span>]), random.randint(<span class="number">0</span>, self.size[<span class="number">1</span>]))</span><br><span class="line">            drawObject.line([begin, end], self.randTextColor())</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> x <span class="keyword">in</span> range(<span class="number">240</span>):</span><br><span class="line">            <span class="keyword">for</span> y <span class="keyword">in</span> range(<span class="number">60</span>):</span><br><span class="line">                tmp = random.randint(<span class="number">0</span>,<span class="number">50</span>)</span><br><span class="line">                <span class="keyword">if</span> tmp&gt;<span class="number">30</span>: <span class="comment">#调整干扰点数量</span></span><br><span class="line">                    drawObject.point((x,y),self.randBdColor())</span><br><span class="line"></span><br><span class="line">        randchar=<span class="string">''</span>  </span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">5</span>):</span><br><span class="line">            rand=self.randChar()</span><br><span class="line">            randchar+=rand</span><br><span class="line">            drawObject.text([<span class="number">50</span>*i+<span class="number">10</span>,<span class="number">10</span>],rand,self.randTextColor(),font=self.font) <span class="comment">#写入字符</span></span><br><span class="line"></span><br><span class="line">        new_image = new_image.filter(ImageFilter.SHARPEN) <span class="comment"># 滤镜    </span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> new_image,randchar</span><br><span class="line"><span class="meta">@app.route('/&lt;filename&gt;')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_file</span><span class="params">(filename)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> send_from_directory(os.getcwd(),filename)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route('/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">index</span><span class="params">()</span>:</span></span><br><span class="line">    test=picture()</span><br><span class="line">    image,code=test.proPicture()</span><br><span class="line">    image.save(<span class="string">'new.jpg'</span>)</span><br><span class="line">    url=<span class="string">"http://127.0.0.1:5000/new.jpg"</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">'&lt;img src='</span>+url+<span class="string">' /&gt;&lt;br/&gt;'</span>+<span class="string">"图中的code为："</span>+code </span><br><span class="line">    <span class="comment">#这里有缓存,需要CTRL+F5才会有效果</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__==<span class="string">"__main__"</span>:</span><br><span class="line">    app.run()</span><br></pre></td></tr></table></figure>
<p>另外,有的前辈会再加入了扭曲图像增加分辨难度</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 图形扭曲参数 </span></span><br><span class="line">params = [<span class="number">1</span> - float(random.randint(<span class="number">1</span>, <span class="number">2</span>)) / <span class="number">100</span>, </span><br><span class="line">              <span class="number">0</span>, </span><br><span class="line">              <span class="number">0</span>, </span><br><span class="line">              <span class="number">0</span>, </span><br><span class="line">              <span class="number">1</span> - float(random.randint(<span class="number">1</span>, <span class="number">10</span>)) / <span class="number">100</span>, </span><br><span class="line">              float(random.randint(<span class="number">1</span>, <span class="number">2</span>)) / <span class="number">500</span>, </span><br><span class="line">              <span class="number">0.001</span>, </span><br><span class="line">              float(random.randint(<span class="number">1</span>, <span class="number">2</span>)) / <span class="number">500</span> </span><br><span class="line">              ] </span><br><span class="line">img = img.transform(size, Image.PERSPECTIVE, params) <span class="comment"># 创建扭曲</span></span><br></pre></td></tr></table></figure>
<p>这里<a href="http://blog.csdn.net/icamera0/article/details/50706666" target="_blank" rel="noopener">有篇文章</a>详细的介绍了下:</p>
<blockquote>
<p>对当前图像进行透视变换，产生给定尺寸的新图像。<br>变量data是一个8元组(a,b,c,d,e,f,g,h)，包括一个透视变换的系数。对于输出图像中的每个像素点，新的值来自于输入图像的位置的(a x + b y + c)/(g x + h y + 1), (d x+ e y + f)/(g x + h y + 1)像素，使用最接近的像素进行近似</p>
</blockquote>
<p>这个的源定义就牵涉到了一个仿射变换,涉及一些数学的计算<br>看得我有点懵逼,就没加到我的代码中,先留坑<br><img src="https://s1.ax1x.com/2018/01/01/pSWRRe.png" alt="效果"></p>
<blockquote>
<p>这个地方现在很多网站会使用另一种回答问题的方式,这个方法的实现<br>我个人感觉也是应该也是相同的手段,只是将随机的字符串改为问题,将验证方式改为答案<br>不过这里或许要把问题和答案存进数据库,更方便点,也才能实现</p>
</blockquote>
<p><img src="https://s1.ax1x.com/2018/01/01/pSWWxH.png" alt="另一种"></p>
<h2 id="短信验证"><a href="#短信验证" class="headerlink" title="短信验证"></a>短信验证</h2><p>有时候想自己是不是出生太晚了。。。。。想写的东西,都能搜到很好的博文,如下:<a href="http://www.cnblogs.com/yueerwanwan0204/p/5460668.html" target="_blank" rel="noopener">flask开发restful api系列(5)-短信验证码</a><br>这里<a href="http://www.yuntongxun.com/doc/rest/sms/3_2_2_2.html" target="_blank" rel="noopener">云通讯</a>是文中所用平台的开发文档,不过平台可以自由选择,结果都是一样<br>这里就简化一下前辈的代码,把关于验证码处理的重点代码撸了出来,用到了Redis,我也趁机学了一波,的确挺好用的</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> redis</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line">phonenumber=<span class="number">188888888</span></span><br><span class="line"><span class="comment">#这里可以利用正则过滤一下电话号码,比如：</span></span><br><span class="line"><span class="comment">#/^(13[0-9]|14[5-9]|15[0-9]|16[6]|17[0-8]|18[0-9]|19[8-9])\d&#123;8&#125;$/</span></span><br><span class="line"></span><br><span class="line">conn=redis.StrictRedis(host=<span class="string">'127.0.0.1'</span>,port=<span class="number">6379</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">producCode</span><span class="params">()</span>:</span></span><br><span class="line">    verifyCode=str(random.randint(<span class="number">100000</span>,<span class="number">999999</span>))</span><br><span class="line"></span><br><span class="line">    pipe=conn.pipeline() <span class="comment">#添加管道,可以一次连接执行多次命令</span></span><br><span class="line">    pipe.set(<span class="string">"phone%s"</span>%phonenumber,verifyCode)</span><br><span class="line">    pipe.expire(<span class="string">"phone%s"</span>%phonenumber,<span class="number">60</span>) <span class="comment">#设置过期时间一分钟</span></span><br><span class="line">    pipe.execute()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">checkCode</span><span class="params">()</span>:</span></span><br><span class="line">    pipe=conn.pipeline() <span class="comment">#添加管道,可以一次连接执行多次命令</span></span><br><span class="line">    pipe.set(<span class="string">'postNum%s'</span>%phonenumber,<span class="string">'0'</span>)</span><br><span class="line">    validate_number = request.get_json().get(<span class="string">'validate_number'</span>)</span><br><span class="line">    pipe.incr(<span class="string">'postNum%s'</span>%phonenumber) <span class="comment">#记录提交次数防止爆破</span></span><br><span class="line">    <span class="keyword">if</span> conn.get(<span class="string">'postNum%s'</span>%phonenumber)&gt;<span class="number">3</span>:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">if</span> validate_number != validate_number_in_redis:</span><br><span class="line">        <span class="keyword">return</span> jsonify(&#123;<span class="string">'code'</span>: <span class="number">0</span>, <span class="string">'message'</span>: <span class="string">'验证没有通过'</span>&#125;)</span><br><span class="line">    pipe.set(<span class="string">'is_validate:%s'</span> % phone_number, <span class="string">'1'</span>) <span class="comment">#通过验证码设置value为1</span></span><br><span class="line">    pipe.expire(<span class="string">'is_validate:%s'</span> % phone_number, <span class="number">120</span>)</span><br><span class="line">    pipe.execute()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> jsonify(&#123;<span class="string">'code'</span>: <span class="number">1</span>, <span class="string">'message'</span>: <span class="string">'验证通过'</span>&#125;)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">postMessage</span><span class="params">()</span>:</span></span><br><span class="line">    result=conn.get(<span class="string">"phone%s"</span>%phonenumber)</span><br><span class="line">    <span class="comment">#此时如果通过验证码,result为1,否则为0</span></span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">#剩下的其他操作</span></span><br></pre></td></tr></table></figure>
<p>这里提到了泄露接口导致验证码爆破的情况,我也添加了一些代码<br>另外就是在某些功能模块,也易出现漏洞,比如修改资料处,验证码不仅仅要与phone一致<br>也要检查用户名的一致性,要不然如果只是通过验证码,用户修改为自己的号码,验证码手机号都通过验证<br>(感觉一般人不会出现这种错误)</p>
<blockquote>
<p>而你的代码又是直接传入用户名进行修改操作,这将可能导致任意用户重置密码<br>或者你的代码直接将phone作为索引进行修改</p>
</blockquote>
<p>参考文章: <a href="https://blog.miguelgrinberg.com/post/restful-authentication-with-flask" target="_blank" rel="noopener">狗书authentication</a><br><a href="http://www.cnblogs.com/weiman3389/p/6224928.html" target="_blank" rel="noopener">杂项之图像处理pillow</a><br><a href="http://pillow-cn.readthedocs.io/zh_CN/latest/handbook/concepts.html#bands" target="_blank" rel="noopener">PIL一些基本概念</a><br><a href="http://www.cnblogs.com/way_testlife/archive/2011/04/20/2022997.html" target="_blank" rel="noopener">PIL中的Image模块</a><br><a href="http://blog.csdn.net/dou_co/article/details/17618319" target="_blank" rel="noopener">Python PIL ImageDraw和ImageFont模块学习</a><br><a href="http://blog.csdn.net/icamera0/article/details/50708888" target="_blank" rel="noopener">Python图像处理库PIL的ImageFilter模块介绍</a><br><a href="http://www.redis.net.cn/" target="_blank" rel="noopener">Redis中文文档</a><br><a href="https://github.com/andymccurdy/redis-py" target="_blank" rel="noopener">redis-py</a></p>

      
    </div>

    
      
      



      
      
    

    
      <footer class="post-footer">
        
          <div class="post-tags">
            
              <a href="/tags/flask/">flask</a>
            
              <a href="/tags/python/">python</a>
            
              <a href="/tags/安全/">安全</a>
            
          </div>
        
        
        
  <nav class="post-nav">
    
      <a class="prev" href="/2017/11/07/多选操作的实现/">
        <i class="iconfont icon-left"></i>
        <span class="prev-text nav-default">多选操作的实现</span>
        <span class="prev-text nav-mobile">上一篇</span>
      </a>
    
    
      <a class="next" href="/2017/10/29/似乎没好好研究过HTTP状态码/">
        <span class="next-text nav-default">似乎没好好研究过HTTP状态码</span>
        <span class="prev-text nav-mobile">下一篇</span>
        <i class="iconfont icon-right"></i>
      </a>
    
  </nav>

      </footer>
    

  </article>


          </div>
          
  <div class="comments" id="comments">
      <div id="disqus_thread">
        <noscript>
          Please enable JavaScript to view the
          <a href="//disqus.com/?ref_noscript">comments powered by Disqus.</a>
        </noscript>
      </div> 
    </div>
  </div>


        </div>
      </main>

      <footer id="footer" class="footer">

  <div class="social-links">
    
      
        
          <a href="https://github.com/bay1" class="iconfont icon-github" title="github"></a>
        
      
    
      
        
          <a href="http://weibo.com/3190704711/profile?topnav=1&wvr=6&is_all=1" class="iconfont icon-weibo" title="weibo"></a>
        
      
    
      
    
      
    
      
    
    
    
  </div>


<div class="copyright">
  <span class="copyright-year">
    
    &copy; 
     
      2016 - 
    
    2018
    <span class="author">bay1</span>
  </span>
</div>
      </footer>

      <div class="back-to-top" id="back-to-top">
        <i class="iconfont icon-up"></i>
      </div>
    </div>

    
  
  <script type="text/javascript">
    var disqus_config = function () {
        this.page.url = 'https://bay1.top/2017/11/04/几种开发时安全验证的实现/';
        this.page.identifier = '2017/11/04/几种开发时安全验证的实现/';
        this.page.title = 'python开发时邮箱验证,短信验证,图片验证码的实现';
    };
    (function() {
    var d = document, s = d.createElement('script');

    s.src = '//https-blog-flywinky-top-1.disqus.com/embed.js';

    s.setAttribute('data-timestamp', +new Date());
    (d.head || d.body).appendChild(s);
    })();  
  </script>



    
  





  
    <script type="text/javascript" src="/lib/jquery/jquery-3.1.1.min.js"></script>
  

  
    <script type="text/javascript" src="/lib/slideout/slideout.js"></script>
  

  
    <script type="text/javascript" src="/lib/fancybox/jquery.fancybox.pack.js"></script>
  


    <script type="text/javascript" src="/js/src/even.js?v=2.6.0"></script>
<script type="text/javascript" src="/js/src/bootstrap.js?v=2.6.0"></script>
<script src="/js/prettify.js"></script>
<script type="text/javascript">
$(document).ready(function(){
 $('pre').addClass('prettyprint');
   prettyPrint();
 })
</script>
  </body>
</html>
